<template>
  <div class="z-virtual-cascader">
    <el-popover
      trigger="click"
      placement="bottom-start"
      ref="popover"
      @show="onShow"
      :visible-arrow="false"
      :disabled="isSearch"
      :popper-class="`el-popper el-cascader__dropdown z-virtual-cascader-popper ${popperClass}`"
    >
      <div slot="reference">
        <el-tooltip
          placement="top"
          :open-delay="1000"
          :disabled="!cloneKeyword"
          :content="cloneKeyword"
        >
          <ZAutocomplete
            type="text"
            :clearable="clearable"
            :readonly="loading"
            v-model="currentLabel"
            :fetch-suggestions="querySearch"
            popper-class="suggestions-popper-class"
            :trigger-on-focus="false"
            
            :debounce="800"
            @select="handleSelect"
            @blur="onBlur"
            @focus="onFocus"
            @clear="onCLear"
            @input="onInput"
            ref="autocomplete"
            :placeholder="placeholder"
          >
            <template v-slot:prefix v-if="loading">
              <i class="el-icon-loading" @click.stop></i>
            </template>
            <template slot-scope="{ item }">
              <slot name="suggestion" v-bind="item">
                <span>
                  {{ item[props.label].join(separator) }}
                </span>
              </slot>
            </template>
          </ZAutocomplete>
        </el-tooltip>
      </div>
      <el-cascader-panel
        ref="panel"
        :key="panelKey"
        v-show="!isSearch"
        v-model="currentValue"
        :options="options"
        :props="currentProps"
        :border="false"
        @change="change"
        :render-label="$scopedSlots.default"
      ></el-cascader-panel>
    </el-popover>
  </div>
</template>
<script>
import { v4 } from 'uuid'
import Clickoutside from 'element-ui/src/utils/clickoutside'
import ZAutocomplete from '@/components/storybook/z-autocomplete'

export default {
  name: 'ZVirtualCascader',
  directives: { Clickoutside },
  components: { ZAutocomplete },
  props: {
    value: {
      type: Array,
      default: () => []
    },
    separator: {
      type: String,
      default: ' > '
    },
    popperClass: String,
    clearable: Boolean,
    disabled: Boolean,
    placeholder: {
      type: String,
      default: 'Select'
    },
    props: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      options: [],
      currentProps: {
        checkStrictly: this.props.checkStrictly,
        value: this.props.value,
        label: this.props.label,
        leaf: this.props.leaf,
        lazy: true,
        lazyLoad: this.lazyLoad,
        virtual: true
      },

      loading: false,

      isSearch: false,
      isEmpty: false,
      currentLabel: undefined,
      cloneKeyword: undefined,
      currentValue: [],
      panelKey: v4()
    }
  },
  computed: {
    placeholderVisible() {
      if (this.currentValue) {
        return this.currentValue.length === 0
      } else {
        return true
      }
    }
  },
  watch: {
    async currentValue() {
      await this.getLabelArray()
      this.loading = false
    }
  },
  created() {
    this.initOptions()
  },

  methods: {
    onShow() {
      this.$refs['autocomplete'].activated = false
      this.panelKey = v4()
    },

    onBlur(el) {
      this.isEmpty = false
      // this.isSearch = false
      // this.$refs['autocomplete'].highlightedIndex = -1
      if (this.currentLabel !== this.cloneKeyword) {
        this.currentLabel = this.cloneKeyword
      } 
    },
    onFocus() {
      this.$refs['autocomplete'].suggestions = []
      this.isSearch = false
    },
    onInput(val) {
      if (val) {
        this.$refs['autocomplete'].activated = true

        this.isSearch = !!val
      }

      if (!this.isSearch) {
        this.$refs['autocomplete'].suggestions = []
        this.$refs['autocomplete'].highlightedIndex = -1
      }
    },
    querySearch(query, callback) {
      this.isSearch = !!query
      if (!query) {
        return callback([])
      }

      let value = query.split(this.separator)
      if (value.length === 1) {
        value = [null, value[0]]
      }
      this.props.lazySearch(value, this.options, list => {
        this.isEmpty = !list.length
        callback(list)
      })
    },
    handleSelect(item) {
      this.$refs.panel.activePath = []
      this.currentValue = item[this.props.value]
      this.currentLabel = item[this.props.label].join(this.separator)
      this.cloneKeyword = this.currentLabel
      this.isSearch = false
      this.$emit('input', this.currentValue)
      this.$emit('change', this.currentValue)
    },

    async initOptions() {
      this.loading = true
      this.props.lazyLoad(0, list => {
        this.$set(this, 'options', list)
        this.currentValue = this.value
      })
    },
    async getLabelArray() {
      const { value = [], label = [] } = await this.getObject(
        this.currentValue || []
      )
      this.currentLabel = label.join(this.separator)
      if (value.length) {
        this.cloneKeyword = this.currentLabel
      }
      this.$emit('input', value)
    },
    async getObject(id) {
      try {
        let options = this.options
        let nameArray = []
        for (let i = 0; i < id.length; i++) {
          let index = options.findIndex(item => {
            return item[this.props.value] === id[i]
          })
          nameArray.push(options[index][this.props.label])
          if (i < id.length - 1 && options[index].children === undefined) {
            let list = new Promise(resolve => {
              this.props.lazyLoad(id[i], list => {
                resolve(list)
              })
            })
            this.$set(options[index], 'children', await list)
            options = options[index].children
          } else {
            options = options[index].children
          }
        }
        return { value: id, label: nameArray }
      } catch (e) {
        this.currentValue = []
        return { value: [], label: [] }
      }
    },
    async lazyLoad(node, resolve) {
      if (node.root) {
        resolve()
      } else if (node.data[this.props.leaf]) {
        resolve()
      } else if (node.data.children) {
        resolve()
      } else {
        this.props.lazyLoad(node.value, list => {
          node.data.children = list

          resolve(list)
        })
      }
    },

    onCLear() {
      this.$refs.panel.clearCheckedNodes()
      this.currentValue = []
      this.cloneKeyword = undefined
      this.$emit('change', this.currentValue)
    },
    change() {
      this.$emit('change', this.currentValue)
      this.$refs.popover.doClose()
    }
  }
}
</script>
<style lang="scss">
.z-virtual-cascader-popper {
  padding: 0 !important;
}

.suggestions-popper-class {
  width: 370px !important;
  min-width: 200px;
  z-index: 99999 !important;

  &.el-autocomplete-suggestion li {
    white-space: inherit;
  }
}
</style>
